// ab包和 resources资源管理器
import { Asset, AssetManager, assetManager, resources } from 'cc';

type BundleInfo = {
    bundle: AssetManager.Bundle;
    refCount: number;
    assets: Map<string, number>;
};

export class ResManager {
    private static _instance: ResManager;
    private _bundles: Map<string, BundleInfo> = new Map();
    private _assets: Map<string, { asset: Asset, isResources: boolean }> = new Map();
    private _resourcesRefs: Map<string, number> = new Map();

    public static get instance(): ResManager {
        if (!this._instance) {
            this._instance = new ResManager();
        }
        return this._instance;
    }

    // 加载AB包或resources资源
    public async load<T extends Asset>(
        path: string,
        type: new () => T,
        bundleName?: string
    ): Promise<T> {
        return bundleName ?
            this._loadFromBundle<T>(bundleName, path, type) :
            this._loadFromResources<T>(path, type);
    }

    // 从AB包加载
    private async _loadFromBundle<T extends Asset>(
        bundleName: string,
        path: string,
        type: new () => T
    ): Promise<T> {
        const assetKey = `${bundleName}/${path}`;

        // 已加载的资源
        if (this._assets.has(assetKey)) {
            const bundleInfo = this._bundles.get(bundleName)!;
            bundleInfo.assets.set(path, (bundleInfo.assets.get(path) || 0) + 1);
            return this._assets.get(assetKey)!.asset as T;
        }

        // 2. 如果资源未加载，但bundle已加载
        if (this._bundles.has(bundleName)) {
            const bundle = this._bundles.get(bundleName)!.bundle;
            return this._loadAssetFromBundle<T>(bundle, bundleName, path, type, assetKey);
        }

        // 3. 如果bundle未加载，先加载bundle
        const bundle = await this._getBundle(bundleName);
        return this._loadAssetFromBundle<T>(bundle, bundleName, path, type, assetKey);
    }

    // 从已加载的bundle中加载资源
    private async _loadAssetFromBundle<T extends Asset>(
        bundle: AssetManager.Bundle,
        bundleName: string,
        path: string,
        type: new () => T,
        assetKey: string
    ): Promise<T> {
        return new Promise((resolve, reject) => {
            bundle.load(path, type, (err, asset) => {
                if (err) {
                    reject(err);
                    return;
                }

                // 手动增加引用计数
                asset.addRef();

                const bundleInfo = this._bundles.get(bundleName)!;

                // 记录资源引用计数
                bundleInfo.assets.set(path, 1);
                this._assets.set(assetKey, { asset, isResources: false });

                resolve(asset as T);
            });
        });
    }

    // 从resources加载
    private async _loadFromResources<T extends Asset>(
        path: string,
        type: new () => T
    ): Promise<T> {
        const refCount = this._resourcesRefs.get(path) || 0;
        this._resourcesRefs.set(path, refCount + 1);

        // 已加载的资源
        if (this._assets.has(path)) {
            return this._assets.get(path)!.asset as T;
        }

        return new Promise((resolve, reject) => {
            resources.load(path, type, (err, asset) => {
                if (err) {
                    this._resourcesRefs.set(path, refCount); // 回滚计数
                    reject(err);
                    return;
                }
                asset.addRef();
                this._assets.set(path, { asset, isResources: true });
                resolve(asset);
            });
        });
    }

    // 释放资源
    public release(path: string, bundleName?: string): void {
        const assetKey = bundleName ? `${bundleName}/${path}` : path;

        if (!this._assets.has(assetKey)) return;

        const assetInfo = this._assets.get(assetKey)!;

        if (assetInfo.isResources) {
            const refCount = this._resourcesRefs.get(path)! - 1;
            this._resourcesRefs.set(path, refCount);

            if (refCount <= 0) {
                assetInfo.asset.decRef();
                this._assets.delete(path);
                this._resourcesRefs.delete(path);
            }
        } else {
            const bundleInfo = this._bundles.get(bundleName!)!;
            const refCount = bundleInfo.assets.get(path)! - 1;

            if (refCount <= 0) {
                assetInfo.asset.decRef();
                bundleInfo.assets.delete(path);
                this._assets.delete(assetKey);
                this._tryReleaseBundle(bundleName!);
            } else {
                bundleInfo.assets.set(path, refCount);
            }
        }
    }

    public async loadBundle(bundleName: string): Promise<AssetManager.Bundle> {
        const bundle = await this._getBundle(bundleName);
        return bundle;
    }

    private async _getBundle(name: string): Promise<AssetManager.Bundle> {
        if (this._bundles.has(name)) {
            const info = this._bundles.get(name)!;
            info.refCount++;
            return info.bundle;
        }

        return new Promise((resolve, reject) => {
            assetManager.loadBundle(name, (err, bundle) => {
                if (err) {
                    reject(err);
                    return;
                }
                this._bundles.set(name, {
                    bundle,
                    refCount: 1,
                    assets: new Map()
                });
                resolve(bundle);
            });
        });
    }

    // 释放AB包
    public releaseBundle(name: string): void {
        if (!this._bundles.has(name)) return;

        const bundleInfo = this._bundles.get(name)!;
        bundleInfo.refCount--;

        if (bundleInfo.refCount <= 0) {
            this._tryReleaseBundle(name);
        }
    }

    private _tryReleaseBundle(name: string): void {
        const bundleInfo = this._bundles.get(name);
        if (!bundleInfo) return;

        if (bundleInfo.refCount <= 0 && bundleInfo.assets.size === 0) {
            assetManager.removeBundle(bundleInfo.bundle);
            this._bundles.delete(name);
        }
    }
}
